/*
Copyright (c) 2005, David M Howard (daveh at dmh2000.com)
All rights reserved.

This product is licensed for use and distribution under the BSD Open Source License.
see the file COPYING for more details.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 

*/

#ifndef __NMEAP_H__
#define __NMEAP_H__

#ifdef __cplusplus
extern "C" {
#endif

/* 
============================================
COMPILE TIME CONFIGURATION CONSTANTS
============================================
*/

/* these constants affect the size of the context object. tweak them as desired but know what you are doing */

/** maximum number of sentence parsers supported */
#define NMEAP_MAX_SENTENCES             4
/** length of sentence name. leave this at 5 unless you really know what you are doing */
#define NMEAP_MAX_SENTENCE_NAME_LENGTH  5
/** max length of a complete sentence. the standard says 82 bytes, but its probably better to go at least 128 since
 * some units don't adhere to the 82 bytes especially for proprietary sentences */
#define NMEAP_MAX_SENTENCE_LENGTH       127
/** max tokens in one sentence. 24 is enough for any standard sentence */
#define NMEAP_MAX_TOKENS                24

#define NMEAP_DEBUG                     0


/* predefined message ID's */
#define     NMEAP_ID_UNKNOWN            0
#define     NMEAP_ID_GGA                1
#define     NMEAP_ID_GLL                2
#define     NMEAP_ID_GSA                3
#define     NMEAP_ID_GSV                4
#define     NMEAP_ID_RMC                5
#define     NMEAP_ID_TXT                6
#define     NMEAP_ID_VTG                7

/* predefined message names */
#define     NMEAP_NAME_GGA              "GGA"
#define     NMEAP_NAME_GLL              "GLL"
#define     NMEAP_NAME_GSA              "GSA"
#define     NMEAP_NAME_GSV              "GSV"
#define     NMEAP_NAME_RMC              "RMC"
#define     NMEAP_NAME_TXT              "TXT"
#define     NMEAP_NAME_VTG              "VTG"

/** user defined parsers should make ID numbers using NMEAP_USER as the base value, plus some increment */
#define     NMEAP_ID_USER                100

/* forward references */
struct nmeap_context;
struct nmeap_sentence;

/* 
============================================
CALLOUTS
============================================
*/

/**
 * sentence callout function type
 * a callout is fired for each registered sentence type
 * the callout gets the object context and a pointer to sentence specific data.
 * the callout must cast the 'sentence_data' to the appropriate type for that callout
 * @param context           nmea object context
 * @param sentence_data     sentence specific data
*/ 
typedef void (*nmeap_callout_t)(struct nmeap_context *context, void *sentence_data, void *user_data);

/**
 * sentence parser function type
 * stored in the object context and called internally when the sentence name matches
 * the specified value
 * the callout gets the object context and a pointer to sentence specific data.
 * the callout must cast the 'sentence_data' to the appropriate type for that callout
 * @param context           nmea object context
 * @param sentence_data     sentence specific data
 * @return id of sentence  (each sentence parser knows its own ID)
*/ 
typedef int (*nmeap_sentence_parser_t)(struct nmeap_context *context, struct nmeap_sentence *sentence);

/* 
============================================
STANDARD SENTENCE DATA STRUCTURES
============================================
*/

/**
 * context for a single sentence 
 */
typedef struct nmeap_sentence 
{
    char                    name[NMEAP_MAX_SENTENCE_NAME_LENGTH + 1];   /* sentence name */
    int                     id;                                         /* sentence type */
    nmeap_sentence_parser_t parser;                                     /* sentence parser */
    nmeap_callout_t         callout;                                    /* callout function */
    void                   *dataout;                                    /* sentence data out */
} nmeap_sentence_t;

/** 
 * parser context
 */
struct nmeap_context 
{
    /** support up to 8 sentences */
    nmeap_sentence_t sentence[NMEAP_MAX_SENTENCES];         /* sentence descriptors */
    int              sentence_count;                        /* number of initialized descriptors */
    
    /** sentence input buffer */
    char             input[NMEAP_MAX_SENTENCE_LENGTH + 1];  /* input line buffer */
    int              input_count;                           /* index into 'input */
    int              input_state;                           /* current lexical scanner state */
    char             input_name[6];                         /* sentence name */
    char             icks;                                  /* input checksum    */
    char             ccks;                                  /* computed checksum */
    
    /* tokenization */
    char            *token[NMEAP_MAX_TOKENS];               /* list of delimited tokens */
    int              tokens;                                /* list of tokens */

#if (defined NMEAP_DEBUG) && (NMEAP_DEBUG != 0)     
    /** errors and debug. optimize these as desired */
    unsigned long    msgs;    /* count of good messages */
    unsigned long    err_hdr; /* header error */                            
    unsigned long    err_ovr; /* overrun error */
    unsigned long    err_unk; /* unknown error */
    unsigned long    err_id;  /* bad character in id */
    unsigned long    err_cks; /* bad checksum */
    unsigned long    err_crl; /* expecting cr or lf, got something else */
    char             debug_input[NMEAP_MAX_SENTENCE_LENGTH + 1];     /* input line buffer for debug */
#endif
    
    /** opaque user data */
    void *user_data;
};
typedef struct nmeap_context nmeap_context_t;

typedef struct
{
    unsigned short  year;
    unsigned char   month;
    unsigned char   date;
}nmeap_date_t;

typedef struct
{
    unsigned char   hour;
    unsigned char   minute;
    unsigned char   second;
    unsigned char   msecond;
}nmeap_time_t;

/* 
============================================
METHODS
============================================
*/

/**
 * initialize an NMEA parser. call this function to initialize a user allocated context object
 * @param context       nmea object context. allocated by user statically or dynamically.
 * @param user_data     pointer to user defined data
 * @return 0 if ok, -1 if initialization failed
 */
int nmeap_init(nmeap_context_t *context, void *user_data);

/**
 * register an NMEA sentence parser
 * @param context          nmea object context
 * @param sentence_name    string matching the sentence name for this parser. e.g. "GPGGA". not including the '$'
 * @param sentence_parser  parser function for this sentence
 * @param sentence_callout callout triggered when this sentence is received and parsed. 
 *                         if null, no callout is triggered for this sentence
 * @param sentence_data    user allocated sentence specific data defined by the application. the parser uses
                           this data item to store the extracted data. This data object needs to persist over the life
                           of the parser, so be careful if allocated on the stack. 
 * @return 0 if registered ok, -1 if registration failed
 */
int nmeap_add_parser(nmeap_context_t         *context,
                     const char              *sentence_name,
                     nmeap_sentence_parser_t sentence_parser,
                     nmeap_callout_t         sentence_callout,
                     void                    *sentence_data
                    );

/** 
 * parse a buffer of nmea data.
 * @param context          nmea object context
 * @param buffer          buffer of input characters
 * @param length          [in,out] pointer to length of buffer. on return, contains number of characters not used for
 *                        the current sentence
 * @return -1 if error, 0 if the data did not complete a sentence, sentence code if a sentence was found in the stream
 */
int nmeap_parse_buffer(nmeap_context_t *context, const unsigned char *buffer, int *length);

/** 
 * parse one character of nmea data.
 * @param context          nmea object context
 * @param ch              input character
 * @return -1 if error, 0 if the data did not complete a sentence, sentence code if a sentence was found in the stream  
 */
int nmeap_parse(nmeap_context_t *context, unsigned char ch);


/**
 * Convert UTC time to UNIX timestamp.
 * @param  date             UTC date.
 * @param  time             UTC time.
 * @return UNIX timestamp 
 */
unsigned int nmeap_get_timestamp(const nmeap_date_t *date, const nmeap_time_t *time);

#ifdef __cplusplus
} // extern C
#endif


#endif

